package com.hww.common.util;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.os.Build;

import androidx.annotation.Nullable;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

/**
 * @author hww
 * @date 0005 2024/7/5
 * email: heww@zhongyuntech.cn
 * description:
 */
public final class ActivityUtils {

    private ActivityUtils() {
        throw new UnsupportedOperationException("u can't instantiate me...");
    }

    /**
     * 通过Context获取Activity实例。
     * 该方法尝试从提供的Context中提取Activity。如果Context不是Activity实例，或者提取出的Activity已经不存在（被销毁），
     * 则返回null。此方法主要用于在非Activity上下文中需要访问Activity实例的场景。
     *
     * @param context 上下文对象，可以是任何类型的Context，但最好是Activity实例。
     * @return 如果context是Activity实例且当前存活，则返回该Activity实例；否则返回null。
     */
    @Nullable
    public static Activity getActivityByContext(@Nullable Context context) {
        if (context == null) return null;
        Activity activity = getActivityByContextInner(context);
        if (!isActivityAlive(activity)) return null;
        return activity;
    }

    /**
     * 检查给定的Activity是否处于活跃状态。
     * 活跃状态意味着Activity没有被销毁且没有处于结束状态。
     * 此方法用于在与Activity交互前检查其状态，以避免对已销毁或结束的Activity进行操作，从而导致应用崩溃。
     *
     * @param activity 需要检查的Activity对象。
     * @return 如果Activity处于活跃状态，则返回true；否则返回false。
     */
    public static boolean isActivityAlive(final Activity activity) {
        return activity != null && !activity.isFinishing()
                && (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1 || !activity.isDestroyed());
    }

    /**
     * 检查给定的Context是否属于一个处于活跃状态的Activity。
     * <p>
     * 本方法通过获取Context对应的Activity，并检查该Activity是否处于活跃状态来判断Context是否属于一个活跃的Activity。
     * 主要用于在需要确定Context来源是否为一个正在运行的Activity时使用，以避免因Context类型不当导致的错误。
     *
     * @param context 待检查的Context对象。
     * @return 如果context属于一个处于活跃状态的Activity，则返回true；否则返回false。
     */
    public static boolean isActivityAlive(final Context context) {
        return isActivityAlive(getActivityByContext(context));
    }

    /**
     * 通过内部方法从Context获取Activity。
     * 该方法用于递归地检查给定的Context是否是Activity，或者是否可以通过特定的ContextWrapper层次结构到达Activity。
     *
     * @param context 待检查的Context对象，可以为null。
     * @return 如果找到Activity，则返回该Activity；否则返回null。
     */
    @Nullable
    private static Activity getActivityByContextInner(@Nullable Context context) {
        if (context == null) return null;
        List<Context> list = new ArrayList<>();
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                return (Activity) context;
            }
            Activity activity = getActivityFromDecorContext(context);
            if (activity != null) return activity;
            list.add(context);
            context = ((ContextWrapper) context).getBaseContext();
            if (context == null) {
                return null;
            }
            if (list.contains(context)) {
                // loop context
                return null;
            }
        }
        return null;
    }

    /**
     * 从DecorContext中获取Activity对象。
     * 该方法主要用于解决在某些情况下，无法直接通过Context获取到Activity实例的问题。
     * 由于DecorContext是系统内部类，直接访问其成员变量可能会受到API级别限制，
     * 因此通过反射的方式尝试获取隐藏在其中的Activity实例。
     *
     * @param context 可能是DecorContext的Context对象。
     * @return 如果context是DecorContext并且成功获取到Activity实例，则返回该实例；
     * 否则返回null。
     */
    @Nullable
    private static Activity getActivityFromDecorContext(@Nullable Context context) {
        if (context == null) return null;
        /*判断context是否为DecorContext类型*/
        if (context.getClass().getName().equals("com.android.internal.policy.DecorContext")) {
            try {
                /*通过反射获取DecorContext中的mActivityContext字段*/
                Field mActivityContextField = context.getClass().getDeclaredField("mActivityContext");
                mActivityContextField.setAccessible(true);
                /*从mActivityContext字段中获取WeakReference<Activity>对象，并尝试获取其持有的Activity实例*/
                //noinspection ConstantConditions,unchecked
                return ((WeakReference<Activity>) mActivityContextField.get(context)).get();
            } catch (Exception ignore) {
            }
        }
        return null;
    }

}
